VOISXPath =
{
    lastXpathExpression : new String(""),
    lastTestXpathExpression : new String(""),
    convertXPathToCount: new Boolean(false),
    rootPrefix: new String(""),
    htmlBody: new String(""),
    usingNameSpaces: new Boolean(false),
    newStart: new String(""),
    contextNode: null,
    nsResolver: null,
    usingLoaclName: new Boolean(false),
    
    Initialize : function ()
    {
        if (VOISXPath_defaultNameSpace.length > 0)
        {
            this.usingNameSpaces = true;
            this.rootPrefix = VOISXPath_defaultNameSpace + ":";
        }

        // this.InitializeForDocument();
        this.InitializeForWorkArea();
    },
    
    InitializeForDocument : function()
    {
        this.newStart = "//div[@id=\"" + VOISEditor.wrapperWorkAreaDivId + "\"]";
        if (this.usingNameSpaces)
        {
            this.htmlBody = "/Phtml/Pbody".replace(/P/g, this.rootPrefix);
        }
        else
        {
            this.htmlBody = "/html/body";
        }
        this.contextNode = document;
        // this.nsResolver = document.createNSResolver(this.contextNode);
        this.nsResolver = this.nsResolverFunction;
    },
    
    InitializeForWorkArea : function()
    {
        this.newStart = "."; // "."
        this.newStartDSlash = ".//"; // ".//"
        if (this.usingNameSpaces)
        {
            this.htmlBody = "/Phtml/Pbody".replace(/P/g, this.rootPrefix);
        }
        else
        {
            this.htmlBody = "/html/body";
        }
        this.contextNode = document.getElementById(VOISEditor.wrapperWorkAreaDivId);
        this.nsResolver = this.nsResolverFunction;

        /*
        var node = document.getElementById(VOISEditor.wrapperWorkAreaDivId);
        var parser=new DOMParser();
        var xmldoc = parser.parseFromString(node.outerHtml,"text/xml");
        this.contextNode = xmldoc.documentElement;
        */
    },
    
    nsResolverFunction : function (prefix)
    {
        return VOISXPath.nameSpaces[prefix] || "http://www.w3.org/" + prefix;
        // VOISXPath.nameSpaces[prefix] || "http://www.w3.org/1999/xhtml";
    },
    
    XPathExpression : function(xpath)
    {
        try
        {
            return document.createExpression(
                xpath, 
                // This function is passed a 
                // namespace prefix and returns the URL.
                function(prefix)
                {
                    if (typeof(prefix) == "undefined")
                    {
                        return "";
                    }
                    else if (prefix in VOISXPath.nameSpaces)
                    {
                        return VOISXPath.nameSpaces[prefix];
                    }
                    else
                    {
                        return "";
                    }
                }
            );
        }
        catch (e)
        {
            return null;
        }
    },

    XPathHighlightWebPage : function(id)
    {
        // show an expath expression from a form textarea or test field.

        var node = document.getElementById(id);
        var fullExpression = node.value.trim();

        // show the xpath expression or clear the web page
        if (this.XPathExpressionHighlightWebPage(fullExpression, "red"))
        {
            this.lastXpathExpression = fullExpression;
        }
        return;
    },

    XPathTestHighlightWebPage : function(xpath)
    {
        if (this.convertXPathToCount == false)
        {
            // xpath is not a count
            // hide the old highlights incase the old test is covering one of the old hightlights
            this._XPathHighlightWebPageUndo();
            this._XPathTestHighlightWebPageUndo();
            // reshow the old xpath expression
            this.XPathExpressionHighlightWebPage(this.lastXpathExpression, "red");
            // show the test xpath expression
            if (this._XPathExpressionHighlightWebPage(xpath, "#f600f6") == true)
            {
                this.lastTestXpathExpression = xpath;
            }
            else
            {
                this.lastTestXpathExpression = "";
            }
        }
    },

    XPathHighlightWebPageUndo : function()
    {
        this._XPathHighlightWebPageUndo();
        this._XPathTestHighlightWebPageUndo();
    },

    XPathExpressionHighlightWebPage : function(fullExpression, color)
    {
        // unshow the old xpath expression
        this.XPathHighlightWebPageUndo();
        // show the new one
        return this._XPathExpressionHighlightWebPage(fullExpression, color);
    },

    _XPathHighlightWebPageUndo : function()
    {
        if (this.lastXpathExpression.length > 0)
        {
            this._XPathExpressionHighlightWebPage(this.lastXpathExpression, "green");
            this.lastXpathExpression = "";
        }
    },

    _XPathTestHighlightWebPageUndo : function()
    {
        if (this.lastTestXpathExpression.length > 0)
        {
            this._XPathExpressionHighlightWebPage(this.lastTestXpathExpression, "green");
            this.lastTestXpathExpression = "";
        }
    },
    
    _XPathExpressionHighlightWebPage : function(fullExpression, color)
    {
        var ok = true;
        // clear the last xpath expression and show the new one.

        // highlight the web page with the expath expression
        if (fullExpression.length > 0)
        {
            var expressions = fullExpression.split(" | ");
            for (var i=0; i<expressions.length; i++)
            {
                var exp = expressions[i].trim();
                if (exp.length > 0)
                {
                    if (this._SingleXPathExpressionHighlightWebPage(exp, color) == false)
                    {
                        // bad expath express unhighlight what has been highlighted
                        ok = false;
                        i--;
                        while (i >= 0)
                        {
                            this._SingleXPathExpressionHighlightWebPage(exp, "green");
                            i--;
                        }
                        break;
                    }
                }
            }
        }
        return ok;
    },
    
    _CleanXpathExpression : function(xpathExp)
    {
        var clean = xpathExp.trim();
        if (clean.length > 0)
        {
            if (clean.substring(0, 2) == "//")
            {
                clean = this.newStartDSlash + clean.substring(2);
            }
            /*
            clean = clean.replace(/\(\/\//g, "(" + this.newStartDSlash)
                .replace(/html\:/g, "")
                .replace(/\/html\/body/g, this.newStart);
            */
            clean = clean.replace(/html\:/g, "")
                .replace(/\/html\/body/g, "__new_start__")
                .replace(/\/\/__new_start__/g, "__new_startDS__")
                .replace(/\(\/\//g, "(" + this.newStartDSlash)
                .replace(/__new_start__/g, this.newStart)
                .replace(/__new_startDS__/g, this.newStartDSlash);
        }
        return clean;
    },
    
    IsValidXpathExpressionBoolean : function(fullExpression)
    {
        var xpathClean = new String("");
        var obj = new Object();
        obj.isValid = true;
        obj.isTrue = true;
        obj.message = new String("");
        
        if (fullExpression.indexOf("\/") < 0)
        {
            obj.isValid = false;
            obj.isTrue = false;
            obj.message = "No slash was found.";
            return obj;
        }
        /*
        if (fullExpression.indexOf("html:") < 0)
        {
            obj.isValid = false;
            obj.isTrue = false;
            obj.message = "Every tag name must be prefixed with 'html:'.";
            return obj;
        }
        */
        
        try
        {
            xpathClean = this._CleanXpathExpression(fullExpression);
            var xpathResult = document.evaluate( xpathClean, this.contextNode, this.nsResolver, XPathResult.BOOLEAN_TYPE, null ); 
            if (xpathResult != null)
            {
                obj.isValid = true;
                obj.isTrue = xpathResult.booleanValue;
            }
            else
            {
                obj.isValid = false;
                obj.isTrue = false;
                obj.message = "Unknown error.";
            }
            return obj;
        }
        catch(e)
        {
            obj.isValid = false;
            obj.isTrue = false;
            obj.message =
                "There is an Error in Your XPath Boolean Expression."
                + "<br />"
                + "Here is part of your XPath Boolean Expression:"
                + "<br /><br />"
                + fullExpression.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;")
                + "<br /><br />"
                + "The VOIS Markup Editor transformed your XPath Boolean Expression to the following:"
                + "<br /><br />"
                + xpathClean.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;")
                + "<br /><br />"
                + "This Error Was Found in the transformed XPath Boolean Expression:"
                + "<br />"
                + "You will should find the same error in the your XPath Boolean Expression."
                + "<br /><br />"
                + e.name
                + "<br />"
                + e.message.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;");
            return obj;
        }
        
    },
    
    IsValidXpathExpressionNodeSet : function(fullExpression)
    {
        var obj = new Object();
        obj.isValid = true;
        obj.message = new String("");
        
        if (fullExpression.indexOf("\/") < 0)
        {
            obj.isValid = false;
            obj.message = "No slash was found.";
            return obj;
        }
        /*
        if (fullExpression.indexOf("html:") < 0)
        {
            obj.isValid = false;
            obj.message = "Every tag name must be prefixed with 'html:'.";
            return obj;
        }
        */
            
        var count = 0;
        var expressions = fullExpression.split(" | ");
        for (var i=0; i<expressions.length; i++)
        {
            var exp;
            var xpathClean;
            try
            {
                var exp = expressions[i].trim();
                if (exp.length > 0)
                {
                    count++;
                    xpathClean = this._CleanXpathExpression(exp);
                    var nodeIterator = document.evaluate( xpathClean, this.contextNode, this.nsResolver, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null ); 
                }
            }
            catch(e)
            {
                obj.isValid = false;
                obj.message =
                    "There is an Error in Your XPath Expression."
                    + "<br />"
                    + "Here is part of your XPath Expression:"
                    + "<br /><br />"
                    + exp.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;")
                    + "<br /><br />"
                    + "The VOIS Markup Editor transformed your XPath Expression to the following:"
                    + "<br /><br />"
                    + xpathClean.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;")
                    + "<br /><br />"
                    + "This Error Was Found in the transformed XPath Expression:"
                    + "<br />"
                    + "You will should find the same error in the your Xpath Expression."
                    + "<br /><br />"
                    + e.name
                    + "<br />"
                    + e.message.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;");
                return obj;
            }
        }
        if (count == 0)
        {
            obj.isValid == false;
            obj.message = "No expressions were found.";
        }

        return obj;
    },
    
    _SingleXpathExpressionNodeSet : function(xpathExp, showAlert)
    {
        var xpathClean = this._CleanXpathExpression(xpathExp);
        if (xpathClean.length > 0)
        {
            try
            {
                return document.evaluate( xpathClean, this.contextNode, this.nsResolver, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ); 
            }
            catch(e)
            {
                if (showAlert && VOISEditor.alerting == false)
                {
                    VOISEditor.alerting = true;
                    alert(
                        "Bad Node Set Expression:\r\n\r\nOriginal: \r\n"
                        + xpathExp
                        + "\r\n\r\nClean: "
                        + xpathClean
                        + "\r\n\r\nError: " + e.name
                        + "\r\n" + e.message);
                    // setTimeout(function(){VOISEditor.alerting = false;}, 2000);
                    setTimeout("VOISEditor.alerting = false;", 2000);
                }
                return null;                
            }
        }
        return null;
    },
    
    _SingleXpathExpressionCount : function(xpathExp, showAlert)
    {
        var xpathClean = this._CleanXpathExpression(xpathExp);
        if (xpathClean.length > 0)
        {
            try
            {
                return document.evaluate( xpathClean, this.contextNode, this.nsResolver, XPathResult.NUMBER_TYPE, null ); 
            }
            catch(e)
            {
                if (showAlert)
                {
                    alert(
                        "Bad Numeric Expression:\r\n\r\nOriginal: \r\n"
                        + xpathExp
                        + "\r\n\r\nClean: "
                        + xpathClean
                        + "\r\n\r\nError: " + e.name
                        + "\r\n" + e.message);
                }
                return null;                
            }
        }
        return null;
    },
    
    XpathExpressionIsTrue : function(xpathExp, showAlert)
    {
        
    },
    
    _SingleXpathExpressionIsTrue : function(xpathExp, showAlert)
    {
        var xpathClean = this._CleanXpathExpression(xpathExp);
        if (xpathClean == this.newStart)
        {
            // equivilant to html/body
            return true;
        }
        if (xpathClean.length > 0)
        {
            try
            {
                // xpathClean = "true( " + xpathClean + " )";
                return document.evaluate( xpathClean, this.contextNode, this.nsResolver, XPathResult.BOOLEAN_TYPE, null ); 
            }
            catch(e)
            {
                if (showAlert && VOISEditor.alerting == false)
                {
                    VOISEditor.alerting = true;
                    alert(
                        "Bad Boolean Expression:\r\n\r\nOriginal: \r\n"
                        + xpathExp
                        + "\r\n\r\nClean: "
                        + xpathClean
                        + "\r\n\r\nError: " + e.name
                        + "\r\n" + e.message);
                    // setTimeout(function(){VOISEditor.alerting = false;}, 2000);
                    setTimeout("VOISEditor.alerting = false;", 2000);
                }
                return null;                
            }
        }
        return null;
    },

    _SingleXPathExpressionHighlightWebPage : function(xpathExp, color)
    {
        var i;
        var ok = true;
        var xpathClean = this._CleanXpathExpression(xpathExp);
        var nodes = new Array();
        if (xpathClean.length > 0)
        {
            try
            {
                var nodeIterator = document.evaluate( xpathClean, this.contextNode, this.nsResolver, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null ); 
                var node = nodeIterator.iterateNext();
                while (node)
                {
                    nodes.push(node);
                    node = nodeIterator.iterateNext();
                }
                for (i=0; i<nodes.length; i++)
                {
                    nodes[i].style.borderColor = color;
                }
            }
            catch(e)
            {
                alert(
                    "Bad Expression:\r\n\r\nOriginal: \r\n"
                    + xpathExp
                    + "\r\n\r\nClean: "
                    + xpathClean
                    + "\r\n\r\nError: " + e.name
                    + "\r\n" + e.message);
                ok = false;
            }
        }
        return ok;
    },

	BuildXPath : function(at, level)
	{
		var xpath = new String("");
		var xpathStart = new String("");
		var temp;
		var node = at;
		var i;
		var topNode = document.getElementById(VOISEditor.wrapperWorkAreaDivId);
        var childNode;
        var tName, tagName;
        var idExp;
        var tests = new Array();
        var test = new String("");

		while(node != topNode)
		{
			// node.className;
            tName = node.localName;
            tagName = this.rootPrefix + node.localName;
			if (node.id && node.id != "")
			{
                // found an id so quit
                idExp = "//" + tagName + "[@id=\"" + node.id + "\"]";

		        if (xpath.charAt(xpath.length-1) == ")")
		        {
			        // remove "(" and ")"
			        xpath = xpathStart.substring(1) + idExp + xpath.substr(0, xpath.length-1);
		        }
		        else
		        {
			        xpath = xpathStart + idExp + xpath;
		        }
                return xpath;
            }
            else if (level > 0)
            {
                // be ambiguous for level number of levels
                xpath = "/" + tagName + xpath;
			    node = node.parentNode;
                level--;
            }
			else
			{
	        	tests = new Array();
                // the role
                var role = node.getAttribute("role")
                if (role != undefined && role.length > 0)
                {
                    test = "@role=\"" + role + "\"";
                    tests[tests.length] = test
                }
                // class names
				var patt = /\s+/g;
				var className = node.className.trim().replace(patt, " ");
                // className = className.replace(VOISEditor.markupUnSelectedClassName, "");
                // className = className.replace(VOISEditor.markupSelectedClassName, "");
                className = className.replace(VOISEditor.markupInlineClassName, "").trim();
                if (tests.length > 0 || className.length > 0)
                {
                	if (className.length > 0)
                    {
                        var classes = className.split(" ");
                        for (i=0; i<classes.length; i++)
                        {
                            test = "contains(@class, \"" + classes[i] + "\")";
                            tests[tests.length] = test
                        }
                    }
                    if (tests.length > 0)
                    {
					    temp = xpath;
                        xpath = "/" + tagName + "[" + tests.join(" and ") + "]" + temp;
                    	
                    }
                }
				else
				{
					// no id, no class name
					// find the index for this tag in its parent
					var matches = 0;
                    var matchedAt = 0;
					for (i=0; i<node.parentNode.childNodes.length; i++)
					{
                        childNode = node.parentNode.childNodes[i];
						if (childNode.nodeType == 1)
                        {
                            if (tName == childNode.localName)
                            {
                                matches++;
                                if (childNode == node)
                                {
                                    matchedAt = matches;
                                }
                                if (matches > matchedAt && matchedAt > 0)
                                {
                                    break;
                                }
                            }
                        }
					}
                    if (matches > 1)
                    {
                        xpathStart += "(";
                        xpath = "/" + tagName + ")[" + matchedAt.toString() + "]" + xpath;
                    }
                    else
                    {
                        xpath = "/" + tagName + xpath;
                    }
				}
			    node = node.parentNode;
                level--;
			}
		}
        // fix xpath
		if (xpath.charAt(xpath.length-1) == ")")
		{
			// remove "(" and ")"
			xpath = xpathStart.substring(1) + this.htmlBody + xpath.substr(0, xpath.length-1);
		}
		else
		{
			xpath = xpathStart + this.htmlBody + xpath;
		}
		return xpath;
	}
}